package jmine.tec.utils.collection;

import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

/**
 * Sorts the stored properties when {@link #keys()} or {@link #keySet()} are called. Key-value pairs will be written sorted when any of the
 * store methods is invoked.
 * <p>
 * Invoking {@link #values()} does not sort the entries.
 * 
 * @author jrenaut
 */
public class SortedProperties extends Properties {

    private Comparator<String> comparator;

    private Set<String> sortedKeys = null;

    /**
     * @param comparator the String comparison to be used
     */
    public SortedProperties(Comparator<String> comparator) {
        this.comparator = comparator;
    }

    /**
     * Uses natural String comparison.
     */
    public SortedProperties() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public Enumeration<Object> keys() {
        return new IteratorEnumeration<Object>((Iterator<Object>) this.getSortedKeys().iterator());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public Set<Object> keySet() {
        return (Set<Object>) this.getSortedKeys();
    }

    /**
     * @return they keys sorted
     */
    private synchronized Set<? extends Object> getSortedKeys() {
        if (this.sortedKeys == null) {
            Enumeration<Object> skeys = super.keys();
            this.sortedKeys = new TreeSet<String>(this.comparator); // if comparator null same as empty constructor
            while (skeys.hasMoreElements()) {
                this.sortedKeys.add(skeys.nextElement().toString());
            }
        }

        return Collections.unmodifiableSet(this.sortedKeys);
    }

    /**
     * Resets the state of this {@link SortedProperties}
     */
    private void reset() {
        this.sortedKeys = null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void clear() {
        super.clear();
        this.reset();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized Object put(Object key, Object value) {
        this.reset();
        return super.put(key, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized Object remove(Object key) {
        this.reset();
        return super.remove(key);
    }
}
